#include "bch.h"
#include "common/stringutils.h"

#include <cmath>
#include <bitset>

#include <QDebug>

namespace qrcode{


BCH::BCH()
{
    generatorPolyBits15_5 = {1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1};
    generatorPolyBits18_6 = {1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1};
    generator15_5 = 0x537;
    generator18_6 = 0x1F25;
}

std::vector<int> BCH::Encoding15_5(std::vector<int> format)
{
    //std::vector<int> format = format;
    //1.将format信息加长为15位，再去除左边所有的0
    AddEnd0s(format, 15);
    RemodeLeft0s(format);

    //2.计算纠错位
    /*
     * 步骤：
     *   1）为生成多项式数据串后边加0，使其的长度等于信息数据串
     *   2）异或多项式数据串与信息数据串
     *   3）去掉信息串左边的0
     *   4）当信息串的位数为10时结束
     */
    std::vector<int> genPoly;
    while(format.size() > 10 && IsNot0s(format)){
        genPoly = generatorPolyBits15_5;
        AddEnd0s(genPoly, format.size());
        XOR(format, genPoly);
        RemodeLeft0s(format);
    }

    if(!IsNot0s(format)){
         format = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    }
    //如果信息串结果不足10时，在左边加0，补全10个
    AddBegin0s(format, 10);

    return format;
}

std::vector<int> BCH::Encoding18_6(std::vector<int> format)
{
    //1.将format信息加长为15位，再去除左边所有的0
    AddEnd0s(format, 18);
    RemodeLeft0s(format);


    //2.计算纠错位
    /*
     * 步骤：
     *   1）为生成多项式数据串后边加0，使其的长度等于信息数据串
     *   2）异或多项式数据串与信息数据串
     *   3）去掉信息串左边的0
     *   4）当信息串的位数为12时结束
     */
    std::vector<int> genPoly;
    while(format.size() > 12 && IsNot0s(format)){
        genPoly = generatorPolyBits18_6;
        AddEnd0s(genPoly, format.size());
        XOR(format, genPoly);
        RemodeLeft0s(format);
    }

    if(!IsNot0s(format)){
         format = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    }

    //如果信息串结果不足12时，在左边加0，补全12个
    AddBegin0s(format, 12);

    return format;
}

std::vector<int> BCH::Decoding15_5(std::vector<int> format)
{
    std::vector<int> vec;
    int n = format.size();

    int fmt = 0;
    for(int i = 0; i < n; i++){
        fmt = fmt + int(format.at(i))*int(pow(2, n-i-1));
    }

    int fmt_n = DecodeFormat(fmt);
    if( fmt_n != -1){
        std::bitset<15> bit = fmt_n;
        std::string str = bit.to_string();
        for(int i = 0; i < str.length(); i++){
            vec.push_back(str.at(i));
        }
    }
    return vec;
}

std::vector<int> BCH::Decoding18_6(std::vector<int> ver)
{
    std::vector<int> vec;
    int n = ver.size();

    int vers = 0;
    for(int i = 0; i < n; i++){
        vers = vers + int(ver.at(i))*int(pow(2, n-i-1));
    }

    int vers_n = DecodeVersion(vers);
    if( vers_n != -1){
        std::bitset<18> bit = vers_n;
        std::string str = bit.to_string();
        for(int i = 0; i < str.length(); i++){
            vec.push_back(str.at(i));
        }
    }
    return vec;
}

bool BCH::IsCorrect15_5(std::vector<int> vec)
{
    int fmt = VecToInt(vec);
    if(CodeFormat(fmt)){
        return false;
    } else {
        return true;
    }
    //return IsCorrect(vec, generatorPolyBits15_5);
}

bool BCH::IsCorrect18_6(std::vector<int> vec)
{
    int ver = VecToInt(vec);
    if(CodeVersion(ver)){
        return false;
    } else {
        return true;
    }
    //return IsCorrect(vec, generatorPolyBits18_6);
}

void BCH::AddEnd0s(std::vector<int> &vec, int length)
{
    if(vec.size() < length){
        int n = length - vec.size();
        for(int i = 0; i < n; i++){
            vec.push_back(0);
        }
    }
}

void BCH::AddBegin0s(std::vector<int> &vec, int length)
{
    if(vec.size() < length){
        int n = length-vec.size();
        for(int i = 0; i < n; i++){
            vec.insert(vec.begin(), 0);
        }
    }
}

void BCH::RemodeLeft0s(std::vector<int> &vec)
{
    if(IsNot0s(vec)){
        while(vec.at(0) == 0){
            vec.erase(vec.begin());
        }
    }
}

bool BCH::IsNot0s(std::vector<int> &vec)
{
    for(int i = 0; i < vec.size(); i++){
        if(vec.at(i) == 1){
            return true;
        }
    }
    return false;
}

void BCH::XOR(std::vector<int> &vec, std::vector<int> &vec1)
{
    std::vector<int> v;
    for(int i = 0; i < vec.size(); i++){
        v.push_back(vec.at(i)^vec1.at(i));
    }
    vec = v;
}

/**
 * @brief BCH::CodeFormat
 * @param fmt
 * @return
 * 说明：
 * 1.生成格式信息纠错码
 *   输入的fmt为原格式信息（5位），在传入参数时需左移10位，使其总长度位15位
 *   输出为长度为10的纠错位，用整数表示
 * 2.检查格式信息是否正确
 *   输入的fmt为15位格式信息位
 *   输出：正确时返回0；错误时返回非0
 */
int BCH::CodeFormat(int fmt)
{
    for(int i = 4; i >= 0; i--){
        if(fmt & (1 << (i+10))){
            fmt ^= generator15_5 << i;
        }
    }
    return fmt;
}

/**
 * @brief BCH::CodeVersion
 * @param ver
 * @return
 * 1.生成版本信息纠错码
 *   输入的ver为原版本信息（6位），在传入参数时需左移12位，使其总长度位18位
 *   输出为长度为12的纠错位，用整数表示
 * 2.检查版本信息是否正确
 *   输入的ver为18位版本信息位
 *   输出：正确时返回0；错误时返回非0
 */
int BCH::CodeVersion(int ver)
{
    for(int i = 5; i >= 0; i--){
        if(ver & (1 << (i+12))){
            ver ^= generator18_6 << i;
        }
    }
    return ver;
}

/**
 * @brief BCH::DecodeFormat
 * @param fmt
 * @return
 * 格式信息为5位，即共32个信息串，找出与错误格式信息串的汉明距离最短的，即为正确值
 */
int BCH::DecodeFormat(int fmt)
{
    int best_fmt = -1;
    int best_dist = 15;
    for(int test_fmt = 0; test_fmt < 32; test_fmt++){
        int test_code = (test_fmt << 10) ^ CodeFormat(test_fmt << 10);
        int test_dist = HammingWeight(fmt^test_code);
        if(test_dist < best_dist){
            best_dist = test_dist;
            best_fmt = test_fmt;
        } else if(test_dist == best_dist){ //如果多个码字与fmt距离相同，则都不选
            best_fmt = -1;
        }
    }
    return best_fmt;
}

int BCH::DecodeVersion(int ver)
{
    int best_ver = -1;
    int best_dist = 18;
    for(int test_ver = 0; test_ver < 64; test_ver++){
        int test_code = (test_ver << 12) ^ CodeFormat(test_ver << 12);
        int test_dist = HammingWeight(ver^test_code);
        if(test_dist < best_dist){
            best_dist = test_dist;
            best_ver = test_ver;
        } else if(test_dist == best_dist){ //如果多个码字与fmt距离相同，则都不选
            best_ver = -1;
        }
    }
    return best_ver;
}

/**
* @brief BCH::HammingWeight
* @param x
* @return
* 汉明距离：两个等长位串之间的汉明距离是两个位串对应位置的不同字符的个数。
*/
int BCH::HammingWeight(int x)
{
    int weight = 0;
    while (x > 0){
        weight += x & 1;
        x >>= 1;
    }
    return weight;
}

int BCH::VecToInt(std::vector<int> &vec)
{
    int n = vec.size();

    int num = 0;
    for(int i = 0; i < n; i++){
        num = num + int(vec.at(i))*int(pow(2, n-i-1));
    }
    return num;
}

bool BCH::IsCorrect(std::vector<int> &vec, std::vector<int> vec1)
{
    int n = vec.size() - vec1.size();
    for(int i = 0; i < n; i++){
        RemodeLeft0s(vec);
        AddEnd0s(vec1, vec.size());
        XOR(vec, vec1);
        if(!IsNot0s(vec)){
            return true;
        }
    }
    return false;
}

}
